package model.algorithms.splitting;

import java.util.ArrayList;
import java.util.List;

import model.components.links.link.Link;
import model.components.links.link.VirtualLink;
import model.components.links.link.PhysicalLink;
import model.components.networks.factory.PhysicalNetworksFactory;
import model.components.networks.network.VirtualNetwork;
import model.components.networks.network.PhysicalNetwork;
import model.components.nodes.node.Node;
import model.components.nodes.node.VirtualNode;
import model.components.nodes.node.PhysicalNode;

public class SplittingAlgorithm1
{
	private String name;
	public String getName(){ return name; }
	public void setName( String name ){ this.name = name; }
	
	private List< PhysicalNetwork > physicalNetworks;
	public List< PhysicalNetwork > getPhysicalNetworks(){ return physicalNetworks; }	
	public void setPhysicalNetworks( List< PhysicalNetwork > physicalNetworks )
	{
		this.physicalNetworks = physicalNetworks;
		
		this.setPhysicalNetworksHopsNumber( this.physicalNetworksHopsNumber() );
		this.setPhysicalNetworksInitialCapacities( this.physicalNetworksInitialCapacities() );//
	}
	
	private int physicalNetworksHopsNumber[][];
	public int[][] getPhysicalNetworksHopsNumber(){ return physicalNetworksHopsNumber; }
	public void setPhysicalNetworksHopsNumber( int physicalNetworksHopsNumber[][] ){ this.physicalNetworksHopsNumber = physicalNetworksHopsNumber; }
	private int[][] physicalNetworksHopsNumber()
	{
		int networksNumber = this.getPhysicalNetworks().size();
		int networksHopsNumber[][] = new int[ networksNumber ][ networksNumber ];
		PhysicalNetwork physicalNetwork = new PhysicalNetwork( PhysicalNetworksFactory.NAME_PREFIX );
		//PhysicalNetwork physicalNetwork = new PhysicalNetwork( PhysicalNetworksFactory.NAME_PREFIX, 0, networksNumber, networksNumber, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Network.PERCENTAGE_LNT, 0, 0, 0.50, 0, 0, 0, 0, 0 );
		
		for( Link link: physicalNetwork.getLinks() )
		{
			networksHopsNumber[ link.getFirstNode().getNumber()  ][ link.getSecondNode().getNumber() ] = 1;
			networksHopsNumber[ link.getSecondNode().getNumber() ][ link.getFirstNode().getNumber()  ] = 1;
		}
		
		for( int i = 0; i < networksNumber; i++ )
			for( int j = 0; j < networksNumber; j++ )
				if( i != j && networksHopsNumber[ i ][ j ] != 1 )
					networksHopsNumber[ i ][ j ] = 2;
		
		return networksHopsNumber;
	}
	
	private double physicalNetworksInitialCapacities[][];
	public double[][] getPhysicalNetworksInitialCapacities(){ return physicalNetworksInitialCapacities; }
	public void setPhysicalNetworksInitialCapacities( double physicalNetworksInitialCapacities[][] ){ this.physicalNetworksInitialCapacities = physicalNetworksInitialCapacities; }
	private double[][] physicalNetworksInitialCapacities()
	{
		double physicalNetworksInitialCapacities[][] = new double[ this.getPhysicalNetworks().size() ][ 4 ];
		int i = 0;
		
		for( PhysicalNetwork physicalNetwork: this.getPhysicalNetworks() )
			physicalNetworksInitialCapacities[ i++ ] = this.physicalNetworkInitialCapacities( physicalNetwork );
		
		return physicalNetworksInitialCapacities;
	}
	private double[] physicalNetworkInitialCapacities( PhysicalNetwork physicalNetwork )
	{
		/** physicalNetworkInitialCapacities[ 0 ] = totalRoutersCapacity;
		    physicalNetworkInitialCapacities[ 1 ] = totalSwitchesCapacity;
		    physicalNetworkInitialCapacities[ 2 ] = totalServersCapacity;
			physicalNetworkInitialCapacities[ 3 ] = totalBandwidth; */
		
		double physicalNetworkInitialCapacities[] = new double[ 4 ];
		
		for( Node node: physicalNetwork.getNodes() )
		{
			if( node.getType().equals( Node.ROUTER_TYPE ) )
				physicalNetworkInitialCapacities[ 0 ] += node.getCpu() + node.getMemory();
			
			else if( node.getType().equals( Node.SWITCH_TYPE ) )
				physicalNetworkInitialCapacities[ 1 ] += node.getCpu() + node.getMemory();
			
			else if( node.getType().equals( Node.SERVER_TYPE ) )
				physicalNetworkInitialCapacities[ 2 ] += node.getCpu() + node.getMemory() + node.getDiskSpace();
		}
		
		for( Link link: physicalNetwork.getLinks() )
			physicalNetworkInitialCapacities[ 3 ] += link.getBandwidth();
		
		return physicalNetworkInitialCapacities;
	}
	
	private double physicalNetworksAvailableCapacities[][];
	public double[][] getPhysicalNetworksAvailableCapacities(){ return physicalNetworksAvailableCapacities; }
	public void setPhysicalNetworksAvailableCapacities( double physicalNetworksAvailableCapacities[][] ){ this.physicalNetworksAvailableCapacities = physicalNetworksAvailableCapacities; }
	private double[][] physicalNetworksAvailableCapacities()
	{
		double physicalNetworksAvailableCapacities[][] = new double[ this.getPhysicalNetworks().size() ][ 4 ];
		int i = 0;
		
		for( PhysicalNetwork physicalNetwork: this.getPhysicalNetworks() )
			physicalNetworksAvailableCapacities[ i++ ] = this.physicalNetworkAvailableCapacities( physicalNetwork );
		
		return physicalNetworksAvailableCapacities;
	}
	private double[] physicalNetworkAvailableCapacities( PhysicalNetwork physicalNetwork )
	{
		/** physicalNetworkAvailableCapacities[ 0 ] = totalRoutersCapacity;
		    physicalNetworkAvailableCapacities[ 1 ] = totalSwitchesCapacity;
		    physicalNetworkAvailableCapacities[ 2 ] = totalServersCapacity;
			physicalNetworkAvailableCapacities[ 3 ] = totalBandwidth; */
		
		double physicalNetworkAvailableCapacities[] = new double[ 4 ];
		
		for( PhysicalNode physicalNode: physicalNetwork.getPhysicalNodes() )
		{
			if( physicalNode.getType().equals( Node.ROUTER_TYPE ) )
				physicalNetworkAvailableCapacities[ 0 ] += physicalNode.getAvailableCpu() + physicalNode.getAvailableMemory();
			
			else if( physicalNode.getType().equals( Node.SWITCH_TYPE ) )
				physicalNetworkAvailableCapacities[ 1 ] += physicalNode.getAvailableCpu() + physicalNode.getAvailableMemory();
			
			else if( physicalNode.getType().equals( Node.SERVER_TYPE ) )
				physicalNetworkAvailableCapacities[ 2 ] += physicalNode.getAvailableCpu() + physicalNode.getAvailableMemory() + physicalNode.getAvailableDiskSpace();
		}
		
		for( PhysicalLink physicalLink: physicalNetwork.getPhysicalLinks() )
			physicalNetworkAvailableCapacities[ 3 ] += physicalLink.getAvailableBandwidth();
		
		return physicalNetworkAvailableCapacities;
	}
	
	private double physicalNetworksAvailabilityPercentage[][];
	public double[][] getPhysicalNetworksAvailabilityPercentage(){ return physicalNetworksAvailabilityPercentage; }
	public void setPhysicalNetworksAvailabilityPercentage( double[][] physicalNetworksAvailabilityPercentage ){ this.physicalNetworksAvailabilityPercentage = physicalNetworksAvailabilityPercentage; }
	private double[][] physicalNetworksAvailabilityPercentage()
	{
		double physicalNetworksInitialCapacities[][] = this.getPhysicalNetworksInitialCapacities();
		double physicalNetworksAvailableCapacities[][] = this.getPhysicalNetworksAvailableCapacities();
		
		double availabilityPercentage[][] = new double[ this.getPhysicalNetworks().size() ][ 4 ];
		
		for( int i = 0; i < this.getPhysicalNetworks().size(); i++ )
		{
			availabilityPercentage[ i ][ 0 ] = 100 * ( physicalNetworksAvailableCapacities[ i ][ 0 ] / physicalNetworksInitialCapacities[ i ][ 0 ] );
			availabilityPercentage[ i ][ 1 ] = 100 * ( physicalNetworksAvailableCapacities[ i ][ 1 ] / physicalNetworksInitialCapacities[ i ][ 1 ] );
			availabilityPercentage[ i ][ 2 ] = 100 * ( physicalNetworksAvailableCapacities[ i ][ 2 ] / physicalNetworksInitialCapacities[ i ][ 2 ] );
			availabilityPercentage[ i ][ 3 ] = 100 * ( physicalNetworksAvailableCapacities[ i ][ 3 ] / physicalNetworksInitialCapacities[ i ][ 3 ] );
		}
		
		return availabilityPercentage;
	}
	
	private VirtualNetwork virtualNetwork;
	public VirtualNetwork getVirtualNetwork(){ return virtualNetwork; }
	public void setVirtualNetwork( VirtualNetwork virtualNetwork ){ this.virtualNetwork = virtualNetwork; }
	
	private int attributesMatchingNumber[][];
	public int[][] getAttributesMatchingNumber(){ return attributesMatchingNumber; }
	public void setAttributesMatchingNumber( int attributesMatchingNumber[][] ){ this.attributesMatchingNumber = attributesMatchingNumber; }
	private int[][] attributesMatchingNumber()
	{
		/** attributesMatchingNumber[ physicalNetworkNumber ][ virtualNodeNumber ] */
		int attributesMatchingNumber[][] = new int[ this.getPhysicalNetworks().size() ][ this.getVirtualNetwork().getNodes().size() ];
		int i = 0;
		
		for( PhysicalNetwork physicalNetwork: this.getPhysicalNetworks() )
		{
			for( VirtualNode virtualNode: this.getVirtualNetwork().getVirtualNodes() )
				for( PhysicalNode physicalNode: physicalNetwork.getPhysicalNodes() )
					if( virtualNode.getType().equals( physicalNode.getType() ) && 
						virtualNode.getOperatingSystem().equals( physicalNode.getOperatingSystem() ) && 
						virtualNode.getVirtualEnvironment().equals( physicalNode.getVirtualEnvironment() ) && 
						virtualNode.getCpu() < physicalNode.getAvailableCpu() && 
						virtualNode.getMemory() < physicalNode.getAvailableMemory() )
					{
						if( virtualNode.getType().equals( Node.ROUTER_TYPE ) || virtualNode.getType().equals( Node.SWITCH_TYPE ) )
							attributesMatchingNumber[ i ][ virtualNode.getNumber() ] += 1;
						
						else if( virtualNode.getType().equals( Node.SERVER_TYPE ) && virtualNode.getDiskSpace() < physicalNode.getAvailableDiskSpace() )
							attributesMatchingNumber[ i ][ virtualNode.getNumber() ] += 1;
					}
			
			i++;
		}
		
		return attributesMatchingNumber;
	}
	
	private double nodesMappingCost[][];
	public double[][] getNodesMappingCost(){ return nodesMappingCost; };
	public void setNodesMappingCost( double nodesMappingCost[][] ){ this.nodesMappingCost = nodesMappingCost; };
	private double[][] nodesMappingCost()
	{
		/** nodesMappingCost[ physicalNetworkNumber ][ virtualNodeNumber ] */
		int attributesMatchingNumber[][] = this.getAttributesMatchingNumber();
		double physicalNetworksAvailabilityPercentage[][] = this.getPhysicalNetworksAvailabilityPercentage();
		double nodesMappingCost[][] = new double[ this.getPhysicalNetworks().size() ][ this.getVirtualNetwork().getNodes().size() ];
		
		for( int i = 0; i < this.getPhysicalNetworks().size(); i++ )
			for( VirtualNode virtualNode: this.getVirtualNetwork().getVirtualNodes() )
			{
				int virtualNodesNumber = virtualNode.getNumber();
				
				if( attributesMatchingNumber[ i ][ virtualNodesNumber ] > 0 )
				{
					if( virtualNode.getType().equals( Node.ROUTER_TYPE ) )
						nodesMappingCost[ i ][ virtualNodesNumber ] = 1 / ( physicalNetworksAvailabilityPercentage[ i ][ 0 ] * attributesMatchingNumber[ i ][ virtualNodesNumber ] );
					
					else if( virtualNode.getType().equals( Node.SWITCH_TYPE ) )
						nodesMappingCost[ i ][ virtualNodesNumber ] = 1 / ( physicalNetworksAvailabilityPercentage[ i ][ 1 ] * attributesMatchingNumber[ i ][ virtualNodesNumber ] );
					
					else if( virtualNode.getType().equals( Node.SERVER_TYPE ) )
						nodesMappingCost[ i ][ virtualNodesNumber ] = 1 / ( physicalNetworksAvailabilityPercentage[ i ][ 2 ] * attributesMatchingNumber[ i ][ virtualNodesNumber ] );
				}
				else
				{				
					nodesMappingCost[ i ][ virtualNodesNumber ] = Double.MAX_VALUE;
				}
			}
		
		return nodesMappingCost;
	}
	
	private double linksMappingCost[][][][];
	public double[][][][] getLinksMappingCost(){ return linksMappingCost; }
	public void setLinksMappingCost( double[][][][] linksMappingCost ){ this.linksMappingCost = linksMappingCost; }
	private double[][][][] linksMappingCost()
	{
		int physicalNetworksNumber = this.getPhysicalNetworks().size();
		int virtualNodesNumber = this.getVirtualNetwork().getNodes().size();
		
		int attributesMatchingNumber[][] = this.getAttributesMatchingNumber();
		int maxAttributesMatchingNumber = 0;
		
		for( int i = 0; i < physicalNetworksNumber; i++ )
			for( int j = 0; j < virtualNodesNumber; j++ )
				if( attributesMatchingNumber[ i ][ j ] > maxAttributesMatchingNumber )
					maxAttributesMatchingNumber = attributesMatchingNumber[ i ][ j ];
		
		double      vlanLinkCost = (double) 1 / ( (int)( 1 + Math.random() * maxAttributesMatchingNumber ) );
		double     sonetLinkCost = (double) 1 / ( (int)( 1 + Math.random() * maxAttributesMatchingNumber ) );
		double eight0211LinkCost = (double) 1 / ( (int)( 1 + Math.random() * maxAttributesMatchingNumber ) );
		
		double linksMappingCost[][][][] = new double[ physicalNetworksNumber ][ physicalNetworksNumber ][ virtualNodesNumber ][ virtualNodesNumber ];
		
		for( int i = 0; i < physicalNetworksNumber; i++ )
			for( VirtualLink virtualLink: this.getVirtualNetwork().getVirtualLinks() )
			{
				int k = virtualLink.getFirstNode().getNumber();
				int l = virtualLink.getSecondNode().getNumber();
			
				if( virtualLink.getType().equals( VirtualLink.VLAN_TYPE ) )
				{
					linksMappingCost[ i ][ i ][ k ][ l ] = vlanLinkCost;
					linksMappingCost[ i ][ i ][ l ][ k ] = vlanLinkCost;
				}
				else if( virtualLink.getType().equals( VirtualLink.SONET_TYPE ) )
				{
					linksMappingCost[ i ][ i ][ k ][ l ] = sonetLinkCost;
					linksMappingCost[ i ][ i ][ l ][ k ] = sonetLinkCost;
				}
				else if( virtualLink.getType().equals( VirtualLink.EIGHT0211_TYPE ) )
				{
					linksMappingCost[ i ][ i ][ k ][ l ] = eight0211LinkCost;
					linksMappingCost[ i ][ i ][ l ][ k ] = eight0211LinkCost;
				}
			}
		
		// The cost of leasing an inter-link is 1.5 higher than the cost of leasing an intra link.
		double penalty = 1.5 * this.maximum( vlanLinkCost, sonetLinkCost, eight0211LinkCost );
		double bandwidths[][] = this.getVirtualNetwork().getBandwidths();
		int physicalNetworksHopsNumber[][] = this.getPhysicalNetworksHopsNumber();
		double physicalNetworksAvailabilityPercentage[][] = this.getPhysicalNetworksAvailabilityPercentage();
		
		// Set the link cost
		for( int i = 0; i < physicalNetworksNumber; i++ )
			for( int j = 0; j < physicalNetworksNumber; j++ )
				for( int k = 0; k < virtualNodesNumber; k++ )
					for( int l = 0; l < virtualNodesNumber; l++ )
					{
						if( bandwidths[ k ][ l ] != 0 && i != j )
							linksMappingCost[ i ][ j ][ k ][ l ] = ( Math.max( linksMappingCost[ i ][ i ][ k ][ l ], linksMappingCost[ j ][ j ][ k ][ l ] ) + penalty * physicalNetworksHopsNumber[ i ][ j ] ) / ( ( physicalNetworksAvailabilityPercentage[ i ][ 3 ] + physicalNetworksAvailabilityPercentage[ j ][ 3 ] ) / 2 );
						
						else if( bandwidths[ k ][ l ] != 0 && i == j )
							linksMappingCost[ i ][ j ][ k ][ l ] = linksMappingCost[ i ][ j ][ k ][ l ] / physicalNetworksAvailabilityPercentage[ i ][ 3 ];
						
						else if( bandwidths[ k ][ l ] == 0 )
							linksMappingCost[ i ][ j ][ k ][ l ] = Integer.MAX_VALUE;
					}
		
		return linksMappingCost;
	}
	private double maximum( double x, double y, double z )
	{
		if(      ( x >= y ) && ( x >= z ) )	return x;
		else if( ( y >= x ) && ( y >= z ) ) return y;
		else if( ( z >= x ) && ( z >= y ) ) return z;
		else 								return 0;
	}
	
	public SplittingAlgorithm1( String name )
	{
		this.setName( name );
	}
	
	public List< VirtualNetwork > run( VirtualNetwork virtualNetwork )
 	{
		this.setPhysicalNetworksAvailableCapacities( this.physicalNetworksAvailableCapacities() );//
		this.setPhysicalNetworksAvailabilityPercentage( this.physicalNetworksAvailabilityPercentage() );//
		this.setVirtualNetwork( virtualNetwork );
 		this.setAttributesMatchingNumber( this.attributesMatchingNumber() );
 		this.setNodesMappingCost( this.nodesMappingCost() );//
 		this.setLinksMappingCost( this.linksMappingCost() );//
 		
 		int bestNodesMapping[] = this.bestNodesMapping();
		
 		
 		double coste = this.networkMappingCost( bestNodesMapping );
 		
		if( coste < 2000000 )
		{
			bestNodesMapping = this.nodesMappingCheck( bestNodesMapping );
			
			//////
			
			//////
			
			if( bestNodesMapping != null )
				return this.virtualSubnetworks( bestNodesMapping );
		}
		else
		{
			System.out.println( "Coste: " + coste );
			
			int attributesMatchingNumber[][] = this.getAttributesMatchingNumber();
			
			for( int i = 0; i < this.getPhysicalNetworks().size(); i++ )
				for( VirtualNode virtualNode: this.getVirtualNetwork().getVirtualNodes() )
					System.out.println( "attributesMatchingNumber[" + i + "][" + virtualNode.getNumber() + "]: " + attributesMatchingNumber[ i ][ virtualNode.getNumber() ] );
		}
		
		return null;
	}
	protected int[] bestNodesMapping()
	{
		/** nodesMapping[ requestNodeNumber ] = substrateNetworkNumber; */
		
		return null;
	}
	protected int[] nodesMapping( int nodesMapping[], int trials )
	{
		/** nodesMapping[ requestNodeNumber ] = substrateNetworkNumber; */
		
		VirtualNetwork virtualNetwork = this.getVirtualNetwork();
		double linksMappingCost[][][][] = this.getLinksMappingCost();
		double nodesMappingCost[][] = this.getNodesMappingCost();
		double bandwidths[][] = virtualNetwork.getBandwidths();
		int virtualNodesNumber = virtualNetwork.getNodes().size();
		int physicalNetworksNumber = this.getPhysicalNetworks().size();
		
		for( int h = 0; h < trials; h++ )
		{
			int virtualNodeNumber = (int) ( Math.random() * virtualNodesNumber );
			int physicalNetworkNumber = (int) ( Math.random() * physicalNetworksNumber );
			double networkMappingCost = 0;
			
			for( int i = 0; i < virtualNodesNumber; i++ )
			{
				if( i == virtualNodeNumber )
					networkMappingCost += nodesMappingCost[ physicalNetworkNumber ][ i ];
				else
					networkMappingCost += nodesMappingCost[ nodesMapping[ i ] ][ i ];	
			}
				
			for( int i = 0; i < virtualNodesNumber; i++ )
				for( int j = i; j < virtualNodesNumber; j++ )
					if( bandwidths[ i ][ j ] != 0 )
					{
						if( i == virtualNodeNumber )
							networkMappingCost += linksMappingCost[ physicalNetworkNumber ][ nodesMapping[ j ] ][ i ][ j ];
						
						else if( j == virtualNodeNumber )
							networkMappingCost += linksMappingCost[ nodesMapping[ i ] ][ physicalNetworkNumber ][ i ][ j ];
						
						else if( i != virtualNodeNumber && j != virtualNodeNumber )
							networkMappingCost += linksMappingCost[ nodesMapping[ i ] ][ nodesMapping[ j ] ][ i ][ j ];
					}
			
			double currentNetworkMappingCost = this.networkMappingCost( nodesMapping );
			
			if( networkMappingCost < currentNetworkMappingCost )
				nodesMapping[ virtualNodeNumber ] = physicalNetworkNumber;
		}
		
		return nodesMapping;
	}
	protected double networkMappingCost( int nodesMapping[] )
	{
		int virtualNodesNumber = this.getVirtualNetwork().getNodes().size();
		double virtualNetworkBandwidths[][] = this.getVirtualNetwork().getBandwidths();
		double nodesMappingCost[][] = this.getNodesMappingCost();
		double linksMappingCost[][][][] = this.getLinksMappingCost();
		double networkMappingCost = 0;
		
		for( int i = 0; i < virtualNodesNumber; i++ )
			networkMappingCost += nodesMappingCost[ nodesMapping[ i ] ][ i ];
		
		for( int i = 0; i < virtualNodesNumber; i++ )
			for( int j = i; j < virtualNodesNumber; j++ )
				if( virtualNetworkBandwidths[ i ][ j ] != 0 )
					networkMappingCost += linksMappingCost[ nodesMapping[ i ] ][ nodesMapping[ j ] ][ i ][ j ];
		
		return networkMappingCost;
	}
	
	private int[] nodesMappingCheck( int nodesMapping[] )
	{
		double nodesMappingCost[][] = this.getNodesMappingCost();
		boolean nodesMappingCheck = false;
		
		int attributesMatchingNumber[][] = this.getAttributesMatchingNumber();
		List< List< Integer > > sameRequestNodes = this.sameVirtualNodes();
				
		while( nodesMappingCheck == false )
		{
			nodesMappingCheck = true;
			
			for( int i = 0; i < this.getVirtualNetwork().getNodes().size(); i++ )
				for( int j = 0; j < this.getPhysicalNetworks().size(); j++ )
				{
					int counter = 0;
					
					for( int k = 0; k < sameRequestNodes.get( i ).size(); k++ )
					{
						if( nodesMapping[ sameRequestNodes.get( i ).get( k ) ] == j )
							counter++;
						
						// If the number of identical virtual nodes embedded in the same InP > number of matches,
						// make the cost of the extra nodes infinity
						
						if( counter > attributesMatchingNumber[ j ][ i ] )
						{
							nodesMappingCost[ j ][ sameRequestNodes.get( i ).get( k ) ] = Integer.MAX_VALUE;
							nodesMappingCheck = false;
						}
					}
				}
			
			this.setNodesMappingCost( nodesMappingCost );
			
			// If there is a wrong splitting, re-do the splitting with the new costs
			if( nodesMappingCheck == false )
				nodesMapping = this.bestNodesMapping();
			
			// Compute the new cost
			double networkMappingCost = this.networkMappingCost( nodesMapping );
			
			// If the new cost is infinity embedding is not possible return null 
			if( networkMappingCost > 20000 )
			{
				nodesMappingCheck = true;
				nodesMapping = null;
			}
		}
		
		return nodesMapping;
	}
	private List< List< Integer > > sameVirtualNodes()
	{
		VirtualNetwork virtualNetwork = this.getVirtualNetwork();
		List< List< Integer > > sameVirtualNodes = new ArrayList< List< Integer > >();
		
		for( int i = 0; i < virtualNetwork.getNodes().size(); i++ )
			sameVirtualNodes.add( new ArrayList< Integer >() );
		
		for( VirtualNode virtualNodeA: virtualNetwork.getVirtualNodes() )
			for( VirtualNode virtualNodeB: virtualNetwork.getVirtualNodes() )
				if( virtualNodeA.getType().equals( virtualNodeB.getType() ) && virtualNodeA.getOperatingSystem().equals( virtualNodeB.getOperatingSystem() ) && virtualNodeA.getVirtualEnvironment().equals( virtualNodeB.getVirtualEnvironment() ) )
					sameVirtualNodes.get( virtualNodeA.getNumber() ).add( virtualNodeB.getNumber() );
		
		return sameVirtualNodes;
	}
	
	private List< VirtualNetwork > virtualSubnetworks( int nodesMapping[] )
	{
		VirtualNetwork virtualNetwork = this.getVirtualNetwork();
		List< PhysicalNetwork > physicalNetworks = this.getPhysicalNetworks();
		
		int virtualNodesNumber = virtualNetwork.getNodes().size();
		List< VirtualNetwork > virtualSubnetworks = new ArrayList< VirtualNetwork >();
		String virtualSubnetworksNames[] = new String[ virtualNodesNumber ];
		String physicalNetworksNamePrefix = physicalNetworks.get( 0 ).getNamePrefix();
		
		for( int i = 0; i < virtualNodesNumber; i++ )
			virtualSubnetworksNames[ i ] = physicalNetworksNamePrefix + nodesMapping[ i ];
			
		int i = 0;
		
		for( PhysicalNetwork physicalNetwork: physicalNetworks )
		{
			VirtualNetwork virtualSubnetwork = new VirtualNetwork( virtualNetwork.getName() + "part", i++, virtualNetwork.getStartingDate(), virtualNetwork.getEndingDate() );
			
			for( VirtualNode virtualNode: virtualNetwork.getVirtualNodes() )
				if( virtualSubnetworksNames[ virtualNode.getNumber() ].contains( physicalNetwork.getName() ) )
				{
					virtualSubnetwork.getGraph().addVertex( virtualNode );
					virtualSubnetwork.setPhysicalNetworkName( physicalNetwork.getName() );
				}
			
			virtualSubnetworks.add( virtualSubnetwork );
		}
		
		for( VirtualNetwork virtualSubnetwork: virtualSubnetworks )
			for( VirtualLink virtualLink: virtualNetwork.getVirtualLinks() )
			{
				int checkingLink = 0;
				
				for( VirtualNode virtualNode: virtualSubnetwork.getVirtualNodes() )
				{
					if( virtualNode.getNumber() == virtualLink.getFirstNode().getNumber() )
						checkingLink++;
					
					if( virtualNode.getNumber() == virtualLink.getSecondNode().getNumber() )
						checkingLink++;
				}
				
				if( checkingLink == 2 )
					virtualSubnetwork.addLinkAndNodes( virtualLink, virtualLink.getFirstNode(), virtualLink.getSecondNode() );
			}
		
		for( VirtualNetwork virtualSubnetwork: virtualSubnetworks )
			this.setNumberVirtualNetwork( virtualSubnetwork );
		
		return virtualSubnetworks;
	}
	private void setNumberVirtualNetwork( VirtualNetwork virtualNetwork )
	{
		int i = 0;
		
		for( VirtualNode virtualNode: virtualNetwork.getVirtualNodes() )
			virtualNode.setNumber( i++ );
		
		i = 0;
		
		for( VirtualLink virtualLink: virtualNetwork.getVirtualLinks() )
			virtualLink.setNumber( i++ );
	}
	
}
